﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="BoundingSphereExtensions.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2007, 2008, 2009, 2010 
//       Roland Rosenkranz (Glatzemann@email.de)
// </copyright>
// <license>
//   This file is part of starLiGHT.Core.
//
//   starLiGHT.Core is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Core is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Core. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Core are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 620                   $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2010-12-07 15:23:58 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;

    #endregion

    /// <summary>
    /// Extension methods for the BoundingSphere
    /// </summary>
    public static class BoundingSphereExtensions
    {
#if !ZUNE
        private static VertexBuffer vertBuffer;
        private static VertexDeclaration vertDecl;
        private static BasicEffect effect;
        private static int sphereResolution;

        /// <summary>
        /// Initializes the graphics objects for rendering the spheres. If this method isn't
        /// run manually, it will be called the first time you render a sphere.
        /// </summary>
        /// <param name="graphicsDevice">The graphics device to use when rendering.</param>
        /// <param name="sphereResolution">
        /// The number of line segments to use for each of the three circles.
        /// </param>
        public static void InitializeGraphics(GraphicsDevice graphicsDevice, int sphereResolution)
        {
            BoundingSphereExtensions.sphereResolution = sphereResolution;

#if !XNA4
            vertDecl = new VertexDeclaration(graphicsDevice, VertexPositionColor.VertexElements);
            effect = new BasicEffect(graphicsDevice, null);
#else
            vertDecl = VertexPositionColor.VertexDeclaration;
            effect = new BasicEffect(graphicsDevice);
#endif

            effect.LightingEnabled = false;
            effect.VertexColorEnabled = false;

            VertexPositionColor[] verts = new VertexPositionColor[(sphereResolution + 1) * 3];

            int index = 0;

            float step = MathHelper.TwoPi / (float)sphereResolution;

            // create the loop on the XY plane first
            for (float a = 0f; a <= MathHelper.TwoPi; a += step)
            {
                verts[index++] = new VertexPositionColor(
                    new Vector3((float)Math.Cos(a), (float)Math.Sin(a), 0f),
                    Color.White);
            }

            // next on the XZ plane
            for (float a = 0f; a <= MathHelper.TwoPi; a += step)
            {
                verts[index++] = new VertexPositionColor(
                    new Vector3((float)Math.Cos(a), 0f, (float)Math.Sin(a)),
                    Color.White);
            }

            // finally on the YZ plane
            for (float a = 0f; a <= MathHelper.TwoPi; a += step)
            {
                verts[index++] = new VertexPositionColor(
                    new Vector3(0f, (float)Math.Cos(a), (float)Math.Sin(a)),
                    Color.White);
            }

#if !XNA4
            vertBuffer = new VertexBuffer(graphicsDevice, verts.Length * VertexPositionColor.SizeInBytes, BufferUsage.None);
            vertBuffer.SetData(verts);
#else
            vertBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionColor), verts.Length, BufferUsage.None);
#endif
        }

        /// <summary>
        /// Renders a bounding sphere using different colors for each axis.
        /// </summary>
        /// <param name="sphere">The sphere to render.</param>
        /// <param name="graphicsDevice">The graphics device to use when rendering.</param>
        /// <param name="camera">The camera used to obtain the view and projection matrices.</param>
        /// <param name="colorXY">The color for the XY circle.</param>
        /// <param name="colorXZ">The color for the XZ circle.</param>
        /// <param name="colorYZ">The color for the YZ circle.</param>
        public static void Render(this BoundingSphere sphere, GraphicsDevice graphicsDevice, ICamera3D camera, Color colorXY, Color colorXZ, Color colorYZ)
        {
            if (vertBuffer == null)
            {
                InitializeGraphics(graphicsDevice, 30);
            }

#if !XNA4
            graphicsDevice.VertexDeclaration = vertDecl;
            graphicsDevice.Vertices[0].SetSource(vertBuffer, 0, VertexPositionColor.SizeInBytes);
#else
            graphicsDevice.SetVertexBuffer(vertBuffer);
#endif

            effect.World = Matrix.CreateScale(sphere.Radius) * Matrix.CreateTranslation(sphere.Center);
            effect.View = camera.View;
            effect.Projection = camera.Projection;
            effect.DiffuseColor = colorXY.ToVector3();

#if !XNA4
            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();

                // render each circle individually
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, 0, sphereResolution);

                effect.DiffuseColor = colorXZ.ToVector3();
                effect.CommitChanges();
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, sphereResolution + 1, sphereResolution);

                effect.DiffuseColor = colorYZ.ToVector3();
                effect.CommitChanges();
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, (sphereResolution + 1) * 2, sphereResolution);

                pass.End();
            }

            effect.End();
#else
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                //render each circle individually
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, 0, sphereResolution);

                effect.DiffuseColor = colorXZ.ToVector3();
                pass.Apply();
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, sphereResolution + 1, sphereResolution);

                effect.DiffuseColor = colorYZ.ToVector3();
                pass.Apply();
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, (sphereResolution + 1) * 2, sphereResolution);
            }
#endif
        }

        /// <summary>
        /// Renders a bounding sphere using a single color for all three axis.
        /// </summary>
        /// <param name="sphere">The sphere to render.</param>
        /// <param name="graphicsDevice">The graphics device to use when rendering.</param>
        /// <param name="camera">The camera used to obtain the view and projection matrices.</param>
        /// <param name="color">The color to use for rendering the circles.</param>
        public static void Render(this BoundingSphere sphere, GraphicsDevice graphicsDevice, ICamera3D camera, Color color)
        {
            if (vertBuffer == null)
            {
                InitializeGraphics(graphicsDevice, 30);
            }

#if !XNA4
            graphicsDevice.VertexDeclaration = vertDecl;
            graphicsDevice.Vertices[0].SetSource(vertBuffer, 0, VertexPositionColor.SizeInBytes);
#else
            graphicsDevice.SetVertexBuffer(vertBuffer);
#endif

            effect.World = Matrix.CreateScale(sphere.Radius) * Matrix.CreateTranslation(sphere.Center);
            effect.View = camera.View;
            effect.Projection = camera.Projection;
            effect.DiffuseColor = color.ToVector3();

#if !XNA4
            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();

                // render each circle individually
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, 0, sphereResolution);
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, sphereResolution + 1, sphereResolution);
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, (sphereResolution + 1) * 2, sphereResolution);

                pass.End();
            }

            effect.End();
#else
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                //render each circle individually
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, 0, sphereResolution);
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, sphereResolution + 1, sphereResolution);
                graphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, (sphereResolution + 1) * 2, sphereResolution);
            }
#endif
        }
#endif
    }
}
